package twoD.IsoPfem;

import inf.jlinalg.Vector2D;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import twoD.IsoPfem.EmbeddedCrackModel.ConstantTractionSeparationLaw;
import twoD.IsoPfem.EmbeddedCrackModel.CrackStructure;
import twoD.IsoPfem.EmbeddedCrackModel.EmbeddedCrackCST;
import twoD.IsoPfem.EmbeddedCrackModel.ExponentialTractionSeparationLaw;
import twoD.IsoPfem.EmbeddedCrackModel.ICrackInitiationCondition;
import twoD.IsoPfem.EmbeddedCrackModel.ITractionSeparationLaw;
import twoD.IsoPfem.EmbeddedCrackModel.LoadingFunction;
import twoD.IsoPfem.EmbeddedCrackModel.SimpleRankineIC;
import twoD.IsoPfem.utils.GiDMeshReader;
import twoD.IsoPfem.utils.MeshUtilities;
import twoD.IsoPfem.utils.MeshUtilities.DiagType;

public class MeshLibrary {

	public static Mesh createMesh2(double l, double h, double t, double dt,
			double c) {

		Mesh M = new Mesh();

		Node[][] N1 = MeshUtilities.createRectangularMesh(0, 0, dt, dt,
				(l - t) / 2, c, M, DiagType.DIAG_TYPE_FORWARD);
		Node[][] N2 = MeshUtilities.createRectangularMesh((l - t) / 2 + t, 0,
				dt, dt, (l - t) / 2, c, M, DiagType.DIAG_TYPE_BACKWARD);

		int k = (int) Math.floor(t / dt) - 1;
		Node[] B = new Node[N1[0].length + k + N2[0].length];
		int cnt = 0;
		for (int i = 0; i < N1[0].length; i++)
			B[cnt++] = N1[N1.length - 1][i];

		double x0 = (l - t) / 2;
		double y0 = c;
		for (int i = 0; i < k; i++)
			B[cnt++] = M.addNode(x0 + (i + 1) * dt, y0);

		for (int i = 0; i < N2[0].length; i++)
			B[cnt++] = N2[N2.length - 1][i];

		MeshUtilities.createDistortedMesh(B, dt,
				(int) Math.floor((h - c) / (2 * dt)), M);

		// finalize geometries to the mesh
		for (IFace f : M.getFaces())
			M.addGeometry(new Triangle(f.getNodes()));

		return M;
	}

	public static Mesh createMesh1() {
		// double l = 1;
		// double h = 1;

		// this is to test matlab implementation
		double l = 100;
		double h = 100;

		Mesh mesh = new Mesh();

		Node n0 = mesh.addNode(new Node(0, 0));
		Node n1 = mesh.addNode(new Node(l, h / 2));
		Node n2 = mesh.addNode(new Node(0, h));

		mesh.addGeometry(new Triangle(n0, n1, n2));

		Constraint c1 = new Constraint(false, false);
		Constraint c2 = new Constraint(false, true);
		n0.setConstraint(c2);
		n2.setConstraint(c1);

		Force f = new Force(0, -1e5);
		// Force f = new Force(1e5, -1e3);
		n1.setForce(f);

		return mesh;
	}

	public static Mesh createTriangleMeshFromGiD(String fileName) {
		GiDMeshReader G = new GiDMeshReader(fileName);
		ArrayList<Vector2D> nodes = G.getNodes2d();
		ArrayList<ArrayList<Integer>> elements = G.getElements();

		Mesh mesh = new Mesh();
		for (Vector2D p : nodes)
			mesh.addNode(new Node(p));
		for (ArrayList<Integer> e : elements) {
			Node n1 = mesh.getNodes(e.get(0) - 1);
			Node n2 = mesh.getNodes(e.get(1) - 1);
			Node n3 = mesh.getNodes(e.get(2) - 1);
			mesh.addGeometry(new Triangle(n1, n2, n3));
		}

		return mesh;
	}

	public static Mesh createTriangleMeshFromGiD2(String fileName) {
		GiDMeshReader G = new GiDMeshReader(fileName);
		ArrayList<Vector2D> nodes = G.getNodes2d();
		ArrayList<ArrayList<Integer>> elements = G.getElements();

		Mesh mesh = new Mesh();

		/*
		 * build the set of nodes, edges and faces
		 */
		int n = nodes.size();
		Set<Integer> edgesSet = new HashSet<Integer>();
		Set<int[]> facesSet = new HashSet<int[]>();
		for (ArrayList<Integer> e : elements) {
			int n1id = e.get(0) - 1;
			int n2id = e.get(1) - 1;
			int n3id = e.get(2) - 1;
			int e1id = (n1id < n2id) ? n1id * n + n2id : n2id * n + n1id;
			int e2id = (n2id < n3id) ? n2id * n + n3id : n3id * n + n2id;
			int e3id = (n3id < n1id) ? n3id * n + n1id : n1id * n + n3id;
			edgesSet.add(e1id);
			edgesSet.add(e2id);
			edgesSet.add(e3id);
			facesSet.add(new int[] { n1id, n2id, n3id, e1id, e2id, e3id });
		}
		for (int i = 0; i < nodes.size(); i++)
			mesh.addNode(new Node(nodes.get(i)));
		ArrayList<Integer> edgesList = new ArrayList<Integer>();
		for (int i : edgesSet) {
			int n1 = i / n;
			int n2 = i - n1 * n;
			mesh.addEdge(new StraightEdge(mesh.getNodes(n1), mesh.getNodes(n2)));
			edgesList.add(i);
		}
		for (int[] f : facesSet) {
			int n1id = f[0];
			int n2id = f[1];
			int n3id = f[2];
			int e1id = edgesList.indexOf(f[3]);
			int e2id = edgesList.indexOf(f[4]);
			int e3id = edgesList.indexOf(f[5]);
			IFace F = mesh.addFace(new FlatFace(new Node[] {
					mesh.getNodes(n1id), mesh.getNodes(n2id),
					mesh.getNodes(n3id) }, mesh.getEdges(e1id), mesh
					.getEdges(e2id), mesh.getEdges(e3id)));
			mesh.addGeometry(new Triangle(F));
		}
		return mesh;
	}

	public static CrackStructure createEmbededCrackModel(RealTable Rt, Mesh mesh) {

		CrackStructure cStruct = new CrackStructure(mesh);

		double ft = Rt.getTensileStrength();
		double Gf = Rt.getFractureEnergy();

		double Dnn = Rt.getDnn();
		double Dss = Rt.getDss();
		double ef = Gf / ft;

		for (IGeometry g : mesh.getGeometries()) {
			cStruct.addElement(new EmbeddedCrackCST(Rt, g));
		}

		ICrackInitiationCondition cic = new SimpleRankineIC(ft);
		ITractionSeparationLaw law = new ConstantTractionSeparationLaw(ft);
		// ITractionSeparationLaw law = new ExponentialTractionSeparationLaw(ef,
		// ft);
		// ITractionSeparationLaw law = new LinearTractionSeparationLaw(ef, ft);
		LoadingFunction lf = new LoadingFunction(law, Dnn, Dss, ft, ef);
		cStruct.setCrackInitiationCondition(cic);
		cStruct.setLoadingFunction(lf);
		cStruct.setRealTable(Rt);

		return cStruct;
	}
}
